#ifndef H_RENDERER
#define H_RENDERER

#include "content.h"
#include "actor.h"
#include "brush.h"
#include "particleSystem.h"
#include "button.h"
#include "node.h"
#include "action.h"
#include "textfile.h"
#include "mesh.h"
#include "flexGrid.h"

using namespace msbButton;

#define BUFSIZE 512

struct bone{

    Matrix4f* boneMatrix;
    Matrix4f* invBoneMatrix;
    std::string name;
    bone*      parentBone;
};

struct complexMesh{

    std::vector<int>    vertexCount;
    std::vector<GLuint> vertexBufferObject;
    std::vector<GLuint> normalBufferObject;
    std::vector<GLuint> colorBufferObject;
    std::vector<GLuint> texCoordBufferObject;

    std::vector<GLuint> vertexWeightsObject; //Vertex Bone Weights 1-4
    std::vector<GLuint> boneReferenceObject; //Bone References

    bool bIsSkeletal;
    bool bIsHead;
    bool bVertexColor;

    int boneCount;
    int texCoordPerVertexCount;
    int verticesPerShapeCount;              //TODO: should be "numbers per Vertex count" - 3 or 4
    GLenum  vertexInterpretation;
    std::vector<bone*>   bones;
    Matrix4f* bindShapeMatrix;                     //points to an array of bone matrices (according to number of bones in boneCount)

};

struct textureObject{

    GLuint texture;
    bool bAlpha;
    bool bWrap;
    std::string nextTexture;
    std::string texFilename;
    float frameRate;
};

struct shaderObject{

    GLuint shader;
    std::string vertexShaderFilename;
    std::string fragmentShaderFilename;
};

using namespace std;

class Input;
class ColladaLoader;
class SpriteMeshLoader;

class Renderer{

public:

        //*****************************************
        //Template for creating Actor IDs
        //*****************************************
        template<typename myType>
        void createActorID(myType *ref ){
            actorID *act;
            act=new actorID;
            //Actor
            act->actorReference=ref;
            act->actorType=&(typeid(*ref));
            act->actorSize=sizeof(*ref);
            actorInfo[typeid(*ref).name()]=*act;
            std::cout << "created a reference for " << typeid(*ref).name() << "\n";
            std::cout << "created a reference for " << act->actorType->name() << "\n";
        }

        //*****************************************
        //Template for creating an Actor
        //*****************************************
        template <typename myType>
        void addActor (myType *a){

            a = new myType;
            actorList.push_back(a);
        }

        //*****************************************
        //Template for creating an Actor
        //*****************************************
        template <typename myType>
        void addButton (myType *b){
            Actor *ac;
            ac = new myType;
            msbButton::Button *basePtr;
            basePtr = dynamic_cast< msbButton::Button*>(ac);

            buttonList.push_back(basePtr);

            //add to save list if we are a saveable button!
            if (basePtr->bPermanent)
                saveableButtonList.push_back(basePtr);
        }

        //*****************************************
        //Template for creating a Node
        // Nodes are also represented in the buttonList!
        //*****************************************
        template <typename myType>
        void addNode (myType *n){
            Actor *ac;
            ac = new myType;
            Node *basePtr;
            basePtr = (Node*)ac;
            nodeList.push_back(basePtr);
            buttonList.push_back(basePtr);
        }

        //*****************************************
        //Template for creating an Action
        // Actions are also represented in the buttonList!
        //*****************************************
        template <typename myType>
        void addAction (myType *b){
            Actor *ac;
            ac = new myType;
            Action *basePtr;
            basePtr = dynamic_cast<Action*>(ac);
            buttonList.push_back(basePtr);

        }

        //verion debug

       static Renderer* rendererInstance;
       Input*           input;

       map <string, actorID> actorInfo;    //for RTTY - all types of actors this program has
                                           //and type/size information for them

       GLuint selectBuf[BUFSIZE];          //for picking

       std::string  startSceneFilename;           //stuff that gets loaded in the very beginning
       std::string  library;           //stuff that gets loaded in the very beginning

       //lists
       vector <Actor*> actorList;          //all created actors go here
       vector <Actor*> helperList;         //all created helpers go here

       vector < msbButton::Button*> buttonList;        //all created buttons go here
       vector < msbButton::Button*> saveableButtonList;    //all saveable Buttons go here
       vector <Node*> nodeList;            //all created nodes go here

       map <string, Action*> actionList;    //all actions

       map <string, ObjFile> meshList;     //old Mesh List
       map <string, complexMesh*> vboList;  //new Vertex Buffer Object List
       map <string, textureObject*> textureList;   //all loaded textures go here
       map <string, shaderObject*> shaderList;    //all loaded shaders go here

       string backgroundTex;
       Vector4f backgroundColor;
       string lastShader;


       bool bUseFBO;                       //make sure we only use FBOs if the hardware
       bool bRenderShadow;                 //creates a FBO with a ShadowBuffer
       bool bRenderSceneTexture;           //creates a FBO with the completely rendered Scene
       bool bRenderStereo;                 //render stereoscpic into Framebuffers
       bool bIncludeShadows;               //include Shadows in Rendering
       bool bIncludeTextures;              //include Textures in Rendering

       bool bDepthPass;                    //we need different shaders for the first pass!
       bool bUpdateTransform;              //compute full transformation for each actor - not necessary when doing picking or shadows

       bool bCollisionOnly;                //only draw Collision

       bool bShowMenu;                      //show menu in 3D renderer
       bool bDrawHelpers;                   //draw helper objects (like bones)

       //Physics using ODE
       dWorldID physicsWorld;              // our physics world
       dSpaceID collisionSpace;            //using ODE collision
       dJointGroupID jointGroup;           // a joint group
       dGeomID groundPlane;                //our ground plane
       bool bUpdatePhysics;                //call physicsUpdate or not

       //al and Screen stuff
       bool bFullscreen;
       int screenX,screenY;                //rendered screen width, height - maybe rename..
       int windowX,windowY;                //window width and height
       float nearClip, farClip;            //clipping planes

       float frustumTop,frustumBottom;     //viewFrustum for 3D stuff
       float eyeDistance;                  //eyeDistance for 3D rendering

       int frames;                         //amount of frames rendered
       double currentTime,                 //time since program start,
              deltaTime,                   //time since last frame
              physicsTime;                 //time for physics step (sometimes deltaTime is too small to advance physics...)

       //Control stuff
       float mouseSensitivity;             //mouse sensitivity and general turn speed
       float moveSpeed;                    //general move speed

       float fov;                          //field of view

       Vector3f lightLoc;                  //light Location
       Vector3f ambient;                  //ambient Light

       Content *content;                   //the list of things we create at program start
       ColladaLoader* colladaLoader;            //helper object for loading meshes and animations
       SpriteMeshLoader* spriteMeshLoader;      //helper Object for loading sprite meshes from Drawings
        //Frame Buffer Objects for shadow rendering

        GLuint shadow_tx;                    // the shadow texture
        GLuint shadow_fb;                           // the framebuffer object to render to that texture
        int shadow_size;                           // the shadow map size
        GLuint shadow_db;

        //Frame Buffer Object for Depth Rendering
        GLuint depth_tx;
        GLuint depth_fb;
        int depth_size;
        GLuint depth_db;

        //Frame Buffer Object for Color Rendering
        GLuint scene_tx;
        GLuint scene_fb;
        int scene_size;
        GLuint scene_db;

        //Frame Buffer Object for Right Eye Rendering
        GLuint leftEye_tx;
        GLuint leftEye_fb;
        int leftEye_size;
        GLuint leftEye_db;

        GLuint rightEye_tx;
        GLuint rightEye_fb;
        int rightEye_size;
        GLuint rightEye_db;

        msbButton::Button* leftEyeFBO;                 //2D planes for left and right eye!
        msbButton::Button* rightEyeFBO;

        //Matrices
        Matrix4f projectionMatrix;
        Matrix4f cameraMatrix;
        Matrix4f inverseCameraMatrix;

        Matrix4f cameraProjectionMatrix;
        Matrix4f cameraViewMatrix;
        Matrix4f camViewProjMatrix;

        Matrix4f lightViewMatrix;
        Matrix4f lightProjectionMatrix;
        Matrix4f lightViewProjMatrix;



       Renderer();
       virtual ~Renderer();

       //singleton-ish
       static Renderer* getInstance();

       //***************************************
       //this is stuff we can update easily
       //***************************************
       virtual void fillGlobalLists();                  //for RTTY - generate actorInfo
       virtual void initWindow(int x, int y, char* windowName);  //
       virtual void reDrawScreen(int w, int h);

       static bool ModifyWindowStyle(HWND hWnd, DWORD dwAdd, DWORD dwRemove, BOOL bEx);

       virtual void setup();                      //first thing called when program starts

       virtual void loadPreferences();
       virtual void loadActor(string fileName);

       virtual void createFBO(GLuint* fbObject, GLuint* fbTexture, GLuint* fbDepth, int fbSize, bool bDepth, string name);
       virtual void checkFBOStatus();

       virtual void update();
       virtual void physicsUpdate();


       virtual void draw();

       virtual void drawShadows();
       virtual void applyShadows();

       virtual void drawSceneTexture();
       virtual void drawStereoscopic();

       virtual void draw3D();
       virtual void draw2D();

       virtual void drawActor(Actor* a);
       virtual void drawHelper(Actor* a);
       virtual void drawButton( msbButton::Button* b);

       virtual void drawCube(float scale);
       virtual void drawPlane(float x1,float  y1,float  x2,float  y2, Vector4f color = Vector4f(1.0,1.0,1.0,1.0));

       virtual void setupCamera(bool bCalculateMatrices);

       virtual void setupShading(string shaderName);

       virtual void setupTexturing(string texName, Actor* a=NULL);
       virtual void setupPostProTextures();
       virtual void transformTextureMatrix(Actor* a);

       virtual void transformActorMatrix(Actor* a);

       virtual void drawBackground();

       void pick(int x, int y);                    //get the 3D coordinates from the mouse and also get the actor we're pointing at
          void startPicking(int cursorX, int cursorY);
          void stopPicking(int cursorX, int cursorY);
          void processHits (GLint hits, GLuint buffer[], int cursorX, int cursorY);
          double connectHits (GLint hits, GLuint buffer[]);

       GLuint createNewRenderTarget();
       GLuint LoadTextureRAW( const char * filename, int size, int wrap );
       bool LoadTextureTGA( string filename, bool wrap, bool bAlpha, string texID );

       void drawColladaMesh (complexMesh* myMesh);

       bool loadShader(string vertexShaderFileName, string fragmentShaderFileName, string shaderProgramName);

       void printShaderInfoLog(GLuint obj);
       void printProgramInfoLog(GLuint obj);


    //has to be static from physics!
       static void handleCollisionBetween(void * data, dGeomID o0, dGeomID o1);



      };


#endif
